#include "EPollPoller.h"
#include "Channel.h"
#include "Logger.h"
#include "TimeStamp.h"
#include <cassert>
#include <cstddef>
#include <cstring>
#include <iostream>
#include <sys/epoll.h>
#include <errno.h>
#include <unistd.h>

const int kNew = -1;
const int kAdded = 1;
const int kDeleted = 2;


EPollPoller::EPollPoller(EventLoop *loop)
: Poller(loop)
, _epollfd(epoll_create1(EPOLL_CLOEXEC))
, _events(kInitEventListSize)
{
    if(_epollfd < 0)
    {
        Logger::Critical("EPollPoller::EPollPoller");
    }
}

EPollPoller::~EPollPoller()
{
    ::close(_epollfd);
}

TimeStamp EPollPoller::poll(int timeoutMs, ChannelList* activeChannels) 
{
    Logger::Trace("fd count == {}",_channels.size());
    int evtNums = ::epoll_wait(_epollfd, 
                               &*_events.begin(), 
                               static_cast<int>(_events.size()), 
                               timeoutMs);
    TimeStamp now(TimeStamp::now());
    if(evtNums > 0) //触发事件
    {
        Logger::Trace("{} events happened...", evtNums);
        fillActiveChannels(evtNums, activeChannels);
        if((size_t)evtNums == _events.size()) // 容量满扩容2倍
        {
            _events.resize(_events.size() * 2);
        }
    }
    else if(evtNums == 0) //无事件
    {
        Logger::Trace("nothing happened");
    }
    else
    {
        //error
        Logger::Critical("EPollPoller::poll()");
    }

    return now; // 返回当前时间戳
}

void EPollPoller::fillActiveChannels(int evtNums, ChannelList *activeChannels) const
{
    assert((size_t)evtNums <= _events.size());
    for(int i = 0; i < evtNums; ++i)
    {
        // 从epoll_event上获取关联的Channel对象
        Channel* channel = static_cast<Channel*>(_events[i].data.ptr);
        int fd = channel->fd();
        ChannelMapConIt it = _channels.find(fd);
        assert(it != _channels.end()); // fd已注册
        assert(it->second == channel); // Channel一致
                                       
        //设置触发的事件类型 加入活跃事件列表
        channel->set_revents(_events[i].events);
        activeChannels->push_back(channel);
    }
}

void EPollPoller::removeChannel(Channel* channel) 
{// 释放正在监听或软移除(kAdded||kDeleted)的Channel对象
    // 线程安全 只在io线程调用
    Poller::assertInLoopThread();
    int fd = channel->fd();
    Logger::Trace("fd = {}", fd);
    assert(_channels.find(fd) != _channels.end());
    assert(_channels[fd] == channel);
    assert(channel->isNoneEvent()); // 必须无事件
    int index = channel->index();
    assert(index == kAdded || index == kDeleted); // 必须在红黑树
    size_t n = _channels.erase(fd);
    assert(n == 1);
    if(index == kAdded) // epoll在监听中: 需要从红黑树上删除
    {
        update(EPOLL_CTL_DEL, channel);
    }
    channel->set_index(kNew); // 设为未注册状态 允许后续再次添加到Poller
}
void EPollPoller::updateChannel(Channel *channel)
{
    Poller::assertInLoopThread();
    const int index = channel->index();
    Logger::Trace("fd = {}, events = {}, index = {}", channel->fd(), channel->events(), index);

    if(index == kNew || index == kDeleted)
    {
        int fd = channel->fd();
        if(index == kNew) // 未在Poller上注册的Channel 
        {
            assert(_channels.find(fd) == _channels.end()); // 表中无该fd
            _channels[fd] = channel;
        }
        else // Poller上已删除的Channel 但对象还未销毁(软移除)
        {
            assert(_channels.find(fd) != _channels.end()); // 表中必须存在fd
            assert(_channels[fd] == channel);
        }
        channel->set_index(kAdded); // 设为已注册且正在监听状态
        update(EPOLL_CTL_ADD, channel);
    }
    else // MOD/DEL
    {
        int fd = channel->fd();
        assert(_channels.find(fd) != _channels.end());
        assert(_channels[fd] == channel);
        assert(index == kAdded);
        if(channel->isNoneEvent()) // 无事件 => DEL
        {
            update(EPOLL_CTL_DEL, channel);
            channel->set_index(kDeleted);
        }
        else
        {
            update(EPOLL_CTL_MOD, channel);
        }
    }
}

void EPollPoller::update(int opt, Channel *channel)
{
    struct epoll_event evt;
    memset(&evt, 0, sizeof evt);
    evt.events = channel->events();
    evt.data.ptr = channel;
    int fd = channel->fd();
    Logger::Trace("epoll_ctl>>> opt = {} fd = {} event = {}", optToStr(opt), fd, channel->eventsToString());
    if(::epoll_ctl(_epollfd, opt, fd, &evt) < 0)
    {
        if(opt == EPOLL_CTL_DEL) Logger::Error("epoll_ctl>>> opt = {} fd = {}", optToStr(opt), fd);
        else Logger::Critical("epoll_ctl>>> opt = {} fd = {}", optToStr(opt), fd);
    }
}
const char* EPollPoller::optToStr(int op)
{
    switch (op)
    {
    case EPOLL_CTL_ADD:
        return "ADD";
    case EPOLL_CTL_DEL:
        return "DEL";
    case EPOLL_CTL_MOD:
        return "MOD";
    default:
        assert(false && "ERROR op");
        return "Unknown Operation";
    }
}

